สำรวจรูปแบบ Dependency Injection ขั้นสูงใน FastAPI เพื่อสร้างแอปพลิเคชันที่ขยายขนาดได้ บำรุงรักษาง่าย และทดสอบได้ เรียนรู้วิธีการสร้าง DI Container ที่แข็งแกร่ง
FastAPI Dependency Injection: สถาปัตยกรรม DI Container ขั้นสูง
FastAPI ซึ่งมีการออกแบบที่ใช้งานง่ายและฟีเจอร์ที่ทรงพลัง ได้กลายเป็นเฟรมเวิร์คยอดนิยมสำหรับการสร้าง Web API สมัยใหม่ในภาษา Python จุดแข็งหลักอย่างหนึ่งของมันอยู่ที่การผสานรวมกับ Dependency Injection (DI) ได้อย่างราบรื่น ซึ่งช่วยให้นักพัฒนาสามารถสร้างแอปพลิเคชันที่เชื่อมโยงกันอย่างหลวมๆ (loosely coupled) ทดสอบได้ และบำรุงรักษาง่าย แม้ว่าระบบ DI ที่มาพร้อมกับ FastAPI จะยอดเยี่ยมสำหรับกรณีการใช้งานง่ายๆ แต่โปรเจกต์ที่ซับซ้อนมากขึ้นมักจะได้รับประโยชน์จากสถาปัตยกรรม DI container ที่มีโครงสร้างและขั้นสูงกว่า บทความนี้จะสำรวจกลยุทธ์ต่างๆ ในการสร้างสถาปัตยกรรมดังกล่าว พร้อมทั้งตัวอย่างที่นำไปใช้ได้จริงและข้อมูลเชิงลึกสำหรับการออกแบบแอปพลิเคชันที่แข็งแกร่งและขยายขนาดได้
ทำความเข้าใจ Dependency Injection (DI) และ Inversion of Control (IoC)
ก่อนที่จะลงลึกในสถาปัตยกรรม DI container ขั้นสูง เรามาทำความเข้าใจแนวคิดพื้นฐานกันก่อน:
- Dependency Injection (DI): รูปแบบการออกแบบที่ dependencies (ส่วนประกอบที่ต้องพึ่งพา) ถูกส่งมอบให้กับ component จากแหล่งภายนอก แทนที่จะถูกสร้างขึ้นภายใน สิ่งนี้ส่งเสริมการเชื่อมโยงแบบหลวมๆ (loose coupling) ทำให้ component ง่ายต่อการทดสอบและนำกลับมาใช้ใหม่
- Inversion of Control (IoC): หลักการที่กว้างกว่าซึ่งการควบคุมการสร้างและจัดการ object ถูกสลับด้าน โดยมอบหมายให้เฟรมเวิร์คหรือ container เป็นผู้จัดการ DI เป็นประเภทหนึ่งของ IoC
FastAPI รองรับ DI โดยธรรมชาติผ่านระบบ dependency ของมัน คุณกำหนด dependencies เป็น object ที่สามารถเรียกใช้ได้ (callable) เช่น ฟังก์ชัน, คลาส ฯลฯ และ FastAPI จะทำการ resolve และ inject เข้าไปในฟังก์ชัน endpoint หรือ dependencies อื่นๆ ของคุณโดยอัตโนมัติ
ตัวอย่าง (Basic FastAPI DI):
from fastapi import FastAPI, Depends
app = FastAPI()
# Dependency
def get_db():
db = {"items": []} # Simulate a database connection
try:
yield db
finally:
# Close the database connection (if needed)
pass
# Endpoint with dependency injection
@app.get("/items/")
async def read_items(db: dict = Depends(get_db)):
return db["items"]
ในตัวอย่างนี้ get_db คือ dependency ที่ให้การเชื่อมต่อฐานข้อมูล FastAPI จะเรียกใช้ get_db โดยอัตโนมัติและ inject ผลลัพธ์ (dictionary db) เข้าไปในฟังก์ชัน endpoint read_items
ทำไมต้องใช้ DI Container ขั้นสูง?
DI ที่มาพร้อมกับ FastAPI ทำงานได้ดีสำหรับโปรเจกต์ง่ายๆ แต่เมื่อแอปพลิเคชันมีความซับซ้อนมากขึ้น DI container ที่ซับซ้อนกว่าจะให้ข้อดีหลายประการ:
- การจัดการ Dependency แบบรวมศูนย์: container ที่ถูกออกแบบมาโดยเฉพาะจะทำหน้าที่เป็นแหล่งข้อมูลจริงเพียงแห่งเดียว (single source of truth) สำหรับ dependencies ทั้งหมด ทำให้ง่ายต่อการจัดการและทำความเข้าใจการพึ่งพากันของแอปพลิเคชัน
- การจัดการ Configuration และ Lifecycle: container สามารถจัดการการกำหนดค่าและวงจรชีวิต (lifecycle) ของ dependencies ได้ เช่น การสร้าง singletons, การจัดการการเชื่อมต่อ และการกำจัดทรัพยากร
- ความสามารถในการทดสอบ (Testability): container ขั้นสูงทำให้การทดสอบง่ายขึ้น โดยช่วยให้คุณสามารถแทนที่ dependencies จริงด้วย mock objects หรือ test doubles ได้อย่างง่ายดาย
- การลดการพึ่งพากัน (Decoupling): ส่งเสริมการแยกส่วนประกอบออกจากกันมากขึ้น ลดการพึ่งพาซึ่งกันและกัน และปรับปรุงความสามารถในการบำรุงรักษาโค้ด
- ความสามารถในการขยาย (Extensibility): container ที่ขยายได้ช่วยให้คุณสามารถเพิ่มฟีเจอร์และการผสานรวมที่กำหนดเองได้ตามต้องการ
กลยุทธ์ในการสร้าง DI Container ขั้นสูง
มีหลายแนวทางในการสร้าง DI container ขั้นสูงใน FastAPI นี่คือกลยุทธ์ทั่วไปบางส่วน:
1. การใช้ไลบรารี DI โดยเฉพาะ (เช่น `injector`, `dependency_injector`)
มีไลบรารี DI ที่ทรงพลังหลายตัวสำหรับ Python เช่น injector และ dependency_injector ไลบรารีเหล่านี้มีชุดฟีเจอร์ที่ครอบคลุมสำหรับการจัดการ dependencies รวมถึง:
- Binding: การกำหนดว่า dependencies จะถูก resolve และ inject อย่างไร
- Scopes: การควบคุมวงจรชีวิตของ dependencies (เช่น singleton, transient)
- Configuration: การจัดการการตั้งค่าสำหรับ dependencies
- AOP (Aspect-Oriented Programming): การดักจับการเรียกใช้เมธอดสำหรับ cross-cutting concerns
ตัวอย่างการใช้งาน `dependency_injector`
dependency_injector เป็นตัวเลือกยอดนิยมสำหรับการสร้าง DI container เรามาดูตัวอย่างการใช้งานกัน:
from dependency_injector import containers, providers
from fastapi import FastAPI, Depends
# Define dependencies
class Database:
def __init__(self, connection_string: str):
self.connection_string = connection_string
# Initialize database connection
print(f"Connecting to database: {self.connection_string}")
def get_items(self):
# Simulate fetching items from the database
return [{"id": 1, "name": "Item 1"}, {"id": 2, "name": "Item 2"}]
class UserRepository:
def __init__(self, database: Database):
self.database = database
def get_all_users(self):
# Simulating database request to get all users
return [{"id": "user1", "name": "Alice"},{"id": "user2", "name": "Bob"}]
class Settings:
def __init__(self, database_url):
self.database_url = database_url
# Define container
class Container(containers.DeclarativeContainer):
config = providers.Configuration()
settings = providers.Singleton(Settings, database_url = config.database_url)
database = providers.Singleton(Database, connection_string=config.database_url)
user_repository = providers.Factory(UserRepository, database=database)
# Create FastAPI app
app = FastAPI()
# Configure container (from an environment variable)
container = Container()
container.config.database_url.from_env("DATABASE_URL", default="sqlite:///:memory:")
container.wire([__name__]) # enables injection of dependencies into FastAPI endpoints
# Dependency for FastAPI
def get_user_repository(user_repository: UserRepository = Depends(container.user_repository.provided)) -> UserRepository:
return user_repository
# Endpoint using injected dependency
@app.get("/users/")
async def read_users(user_repository: UserRepository = Depends(get_user_repository)):
return user_repository.get_all_users()
@app.on_event("startup")
async def startup_event():
# Container initialization
container.init_resources()
คำอธิบาย:
- เรากำหนด dependencies ของเรา (
Database,UserRepository,Settings) เป็นคลาส Python ทั่วไป - เราสร้างคลาส
Containerที่สืบทอดจากcontainers.DeclarativeContainerคลาสนี้กำหนด dependencies และ providers ของมัน (เช่นproviders.Singletonสำหรับ singletons,providers.Factoryสำหรับการสร้าง instance ใหม่ทุกครั้ง) - บรรทัด
container.wire([__name__])เปิดใช้งานการ inject dependency เข้าไปใน FastAPI endpoints - ฟังก์ชัน
get_user_repositoryเป็น dependency ของ FastAPI ที่ใช้container.user_repository.providedเพื่อดึง instance ของ UserRepository จาก container - ฟังก์ชัน endpoint
read_usersทำการ inject dependencyUserRepository - `config` ช่วยให้คุณสามารถแยกการกำหนดค่า dependency ออกไปภายนอกได้ ซึ่งสามารถมาจากตัวแปรสภาพแวดล้อม, ไฟล์ config ฯลฯ
- `startup_event` ใช้เพื่อเริ่มต้นทรัพยากรที่จัดการใน container
2. การสร้าง DI Container แบบกำหนดเอง
เพื่อให้สามารถควบคุมกระบวนการ DI ได้มากขึ้น คุณสามารถสร้าง DI container แบบกำหนดเองได้ แนวทางนี้ต้องใช้ความพยายามมากขึ้น แต่ช่วยให้คุณปรับแต่ง container ให้ตรงกับความต้องการเฉพาะของคุณได้
ตัวอย่าง DI Container แบบกำหนดเองเบื้องต้น:
from typing import Callable, Dict, Type, Any
from fastapi import FastAPI, Depends
class Container:
def __init__(self):
self.dependencies: Dict[Type[Any], Callable[..., Any]] = {}
self.instances: Dict[Type[Any], Any] = {}
def register(self, dependency_type: Type[Any], provider: Callable[..., Any]):
self.dependencies[dependency_type] = provider
def resolve(self, dependency_type: Type[Any]) -> Any:
if dependency_type in self.instances:
return self.instances[dependency_type]
if dependency_type not in self.dependencies:
raise Exception(f"Dependency {dependency_type} not registered.")
provider = self.dependencies[dependency_type]
instance = provider()
return instance
def singleton(self, dependency_type: Type[Any], provider: Callable[..., Any]):
self.register(dependency_type, provider)
self.instances[dependency_type] = provider()
# Example Dependencies
class PaymentGateway:
def process_payment(self, amount: float) -> bool:
print(f"Processing payment of ${amount}")
return True # Simulate successful payment
class NotificationService:
def send_notification(self, message: str):
print(f"Sending notification: {message}")
# Example Usage
container = Container()
container.singleton(PaymentGateway, PaymentGateway)
container.singleton(NotificationService, NotificationService)
app = FastAPI()
# FastAPI Dependency
def get_payment_gateway(payment_gateway: PaymentGateway = Depends(lambda: container.resolve(PaymentGateway))):
return payment_gateway
def get_notification_service(notification_service: NotificationService = Depends(lambda: container.resolve(NotificationService))):
return notification_service
@app.post("/purchase/")
async def purchase_item(payment_gateway: PaymentGateway = Depends(get_payment_gateway), notification_service: NotificationService = Depends(get_notification_service)):
if payment_gateway.process_payment(100.0):
notification_service.send_notification("Purchase successful!")
return {"message": "Purchase successful"}
else:
return {"message": "Purchase failed"}
คำอธิบาย:
- คลาส
Containerจัดการ dictionary ของ dependencies และ providers ของมัน - เมธอด
registerลงทะเบียน dependency กับ provider ของมัน - เมธอด
resolveทำการ resolve dependency โดยการเรียก provider ของมัน - เมธอด
singletonลงทะเบียน dependency และสร้าง instance เดียวของมัน - dependencies ของ FastAPI ถูกสร้างขึ้นโดยใช้ฟังก์ชัน lambda เพื่อ resolve dependencies จาก container
3. การใช้ `Depends` ของ FastAPI ร่วมกับ Factory Function
แทนที่จะใช้ DI container เต็มรูปแบบ คุณสามารถใช้ Depends ของ FastAPI ร่วมกับ factory functions เพื่อให้ได้การจัดการ dependency ในระดับหนึ่ง แนวทางนี้ง่ายกว่าการสร้าง container แบบกำหนดเอง แต่ยังคงให้ประโยชน์บางอย่างเหนือกว่าการสร้าง instance ของ dependencies โดยตรงภายในฟังก์ชัน endpoint
from fastapi import FastAPI, Depends
from typing import Callable
# Define Dependencies
class EmailService:
def __init__(self, smtp_server: str):
self.smtp_server = smtp_server
def send_email(self, recipient: str, subject: str, body: str):
print(f"Sending email to {recipient} via {self.smtp_server}: {subject} - {body}")
# Factory function for EmailService
def create_email_service(smtp_server: str) -> EmailService:
return EmailService(smtp_server=smtp_server)
# FastAPI
app = FastAPI()
# FastAPI Dependency, leveraging factory function and Depends
def get_email_service(email_service: EmailService = Depends(lambda: create_email_service(smtp_server="smtp.example.com"))):
return email_service
@app.post("/send-email/")
async def send_email(recipient: str, subject: str, body: str, email_service: EmailService = Depends(get_email_service)):
email_service.send_email(recipient=recipient, subject=subject, body=body)
return {"message": "Email sent!"}
คำอธิบาย:
- เรากำหนด factory function (
create_email_service) ที่สร้าง instance ของ dependencyEmailService - dependency
get_email_serviceใช้Dependsและ lambda เพื่อเรียก factory function และให้ instance ของEmailService - ฟังก์ชัน endpoint
send_emailทำการ inject dependencyEmailService
ข้อควรพิจารณาขั้นสูง
1. Scopes และ Lifecycles
DI container มักมีฟีเจอร์สำหรับการจัดการวงจรชีวิตของ dependencies ขอบเขต (scope) ที่พบบ่อย ได้แก่:
- Singleton: มีการสร้าง instance ของ dependency เพียงครั้งเดียวและนำกลับมาใช้ใหม่ตลอดอายุการใช้งานของแอปพลิเคชัน เหมาะสำหรับ dependencies ที่ไม่มีสถานะ (stateless) หรือมีขอบเขตเป็นแบบ global
- Transient: มีการสร้าง instance ใหม่ของ dependency ทุกครั้งที่มีการร้องขอ เหมาะสำหรับ dependencies ที่มีสถานะ (stateful) หรือต้องการแยกออกจากกัน
- Request: มีการสร้าง instance ของ dependency เพียงครั้งเดียวสำหรับแต่ละคำขอที่เข้ามา เหมาะสำหรับ dependencies ที่ต้องรักษาสถานะภายในบริบทของคำขอเดียว
ไลบรารี dependency_injector มีการรองรับ scopes ในตัว สำหรับ container ที่กำหนดเอง คุณจะต้องสร้างตรรกะการจัดการ scope ด้วยตัวเอง
2. การกำหนดค่า (Configuration)
Dependencies มักต้องการการตั้งค่าต่างๆ เช่น connection strings ของฐานข้อมูล, API keys และ feature flags DI container สามารถช่วยจัดการการตั้งค่าเหล่านี้ได้โดยการให้วิธีการเข้าถึงและ inject ค่า configuration แบบรวมศูนย์
ในตัวอย่างของ dependency_injector, provider config อนุญาตให้กำหนดค่าจากตัวแปรสภาพแวดล้อม สำหรับ container ที่กำหนดเอง คุณสามารถโหลดการกำหนดค่าจากไฟล์หรือตัวแปรสภาพแวดล้อมและเก็บไว้ใน container ได้
3. การทดสอบ (Testing)
หนึ่งในประโยชน์หลักของ DI คือการปรับปรุงความสามารถในการทดสอบ ด้วย DI container คุณสามารถแทนที่ dependencies จริงด้วย mock objects หรือ test doubles ได้อย่างง่ายดายระหว่างการทดสอบ
ตัวอย่าง (การทดสอบด้วย `dependency_injector`):
import pytest
from unittest.mock import MagicMock
from dependency_injector import containers, providers
from fastapi import FastAPI, Depends
from fastapi.testclient import TestClient
# Define dependencies (same as before)
class Database:
def __init__(self, connection_string: str):
self.connection_string = connection_string
def get_items(self):
return [{"id": 1, "name": "Item 1"}, {"id": 2, "name": "Item 2"}]
class UserRepository:
def __init__(self, database: Database):
self.database = database
def get_all_users(self):
return [{"id": "user1", "name": "Alice"},{"id": "user2", "name": "Bob"}]
class Settings:
def __init__(self, database_url):
self.database_url = database_url
# Define container (same as before)
class Container(containers.DeclarativeContainer):
config = providers.Configuration()
settings = providers.Singleton(Settings, database_url = config.database_url)
database = providers.Singleton(Database, connection_string=config.database_url)
user_repository = providers.Factory(UserRepository, database=database)
# Create FastAPI app (same as before)
app = FastAPI()
# Configure container (from an environment variable)
container = Container()
container.config.database_url.from_env("DATABASE_URL", default="sqlite:///:memory:")
container.wire([__name__]) # enables injection of dependencies into FastAPI endpoints
# Dependency for FastAPI
def get_user_repository(user_repository: UserRepository = Depends(container.user_repository.provided)) -> UserRepository:
return user_repository
# Endpoint using injected dependency (same as before)
@app.get("/users/")
async def read_users(user_repository: UserRepository = Depends(get_user_repository)):
return user_repository.get_all_users()
@app.on_event("startup")
async def startup_event():
# Container initialization
container.init_resources()
# Test
@pytest.fixture
def test_client():
# Override the database dependency with a mock
database_mock = MagicMock(spec=Database)
database_mock.get_items.return_value = [{"id": 3, "name": "Test Item"}]
user_repository_mock = MagicMock(spec = UserRepository)
user_repository_mock.get_all_users.return_value = [{"id": "test_user", "name": "Test User"}]
# Override container with mock dependencies
container.user_repository.override(providers.Factory(lambda: user_repository_mock))
with TestClient(app) as client:
yield client
container.user_repository.reset()
def test_read_users(test_client: TestClient):
response = test_client.get("/users/")
assert response.status_code == 200
assert response.json() == [{"id": "test_user", "name": "Test User"}]
คำอธิบาย:
- เราสร้าง mock object สำหรับ dependency
Databaseโดยใช้MagicMock - เราทำการ override provider
databaseใน container ด้วย mock object โดยใช้container.database.override() - ฟังก์ชันทดสอบ
test_read_itemsตอนนี้จะใช้ dependency ฐานข้อมูลที่เป็น mock - หลังจากการทดสอบเสร็จสิ้น มันจะรีเซ็ต dependency ที่ถูก override ใน container กลับเป็นเหมือนเดิม
4. Asynchronous Dependencies
FastAPI ถูกสร้างขึ้นบนพื้นฐานของการเขียนโปรแกรมแบบอะซิงโครนัส (async/await) เมื่อทำงานกับ dependencies ที่เป็นอะซิงโครนัส (เช่น การเชื่อมต่อฐานข้อมูลแบบอะซิงโครนัส) ต้องแน่ใจว่า DI container และ dependency providers ของคุณรองรับการทำงานแบบอะซิงโครนัส
ตัวอย่าง (Asynchronous Dependency ด้วย `dependency_injector`):
import asyncio
from dependency_injector import containers, providers
from fastapi import FastAPI, Depends
# Define asynchronous dependency
class AsyncDatabase:
def __init__(self, connection_string: str):
self.connection_string = connection_string
async def connect(self):
print(f"Connecting to database: {self.connection_string}")
await asyncio.sleep(0.1) # Simulate connection time
async def fetch_data(self):
await asyncio.sleep(0.1) # Simulate database query
return [{"id": 1, "name": "Async Item 1"}]
# Define container
class Container(containers.DeclarativeContainer):
config = providers.Configuration()
database = providers.Singleton(AsyncDatabase, connection_string=config.database_url)
# Create FastAPI app
app = FastAPI()
# Configure container
container = Container()
container.config.database_url.from_env("DATABASE_URL", default="sqlite:///:memory:")
container.wire([__name__])
# Dependency for FastAPI
async def get_async_database(database: AsyncDatabase = Depends(container.database.provided)) -> AsyncDatabase:
await database.connect()
return database
# Endpoint using injected dependency
@app.get("/async-items/")
async def read_async_items(database: AsyncDatabase = Depends(get_async_database)):
data = await database.fetch_data()
return data
@app.on_event("startup")
async def startup_event():
# Container initialization
container.init_resources()
คำอธิบาย:
- คลาส
AsyncDatabaseกำหนดเมธอดอะซิงโครนัสโดยใช้asyncและawait - dependency
get_async_databaseถูกกำหนดให้เป็นฟังก์ชันอะซิงโครนัสเช่นกัน - ฟังก์ชัน endpoint
read_async_itemsถูกระบุว่าเป็นasyncและรอผลลัพธ์จากdatabase.fetch_data()
การเลือกแนวทางที่เหมาะสม
แนวทางที่ดีที่สุดในการสร้าง DI container ขั้นสูงขึ้นอยู่กับความซับซ้อนของแอปพลิเคชันและข้อกำหนดเฉพาะของคุณ:
- สำหรับโปรเจกต์ขนาดเล็กถึงขนาดกลาง: DI ที่มาพร้อมกับ FastAPI หรือแนวทาง factory function ร่วมกับ
Dependsอาจเพียงพอ - สำหรับโปรเจกต์ขนาดใหญ่และซับซ้อนมากขึ้น: ไลบรารี DI โดยเฉพาะเช่น
dependency_injectorจะมีชุดฟีเจอร์ที่ครอบคลุมสำหรับการจัดการ dependencies - สำหรับโปรเจกต์ที่ต้องการการควบคุมกระบวนการ DI อย่างละเอียด: การสร้าง DI container แบบกำหนดเองอาจเป็นตัวเลือกที่ดีที่สุด
สรุป
Dependency injection เป็นเทคนิคที่ทรงพลังสำหรับการสร้างแอปพลิเคชันที่ขยายขนาดได้ บำรุงรักษาง่าย และทดสอบได้ แม้ว่าระบบ DI ที่มาพร้อมกับ FastAPI จะยอดเยี่ยมสำหรับกรณีการใช้งานง่ายๆ แต่สถาปัตยกรรม DI container ขั้นสูงสามารถให้ประโยชน์อย่างมากสำหรับโปรเจกต์ที่ซับซ้อนมากขึ้น โดยการเลือกแนวทางที่เหมาะสมและใช้ประโยชน์จากฟีเจอร์ของไลบรารี DI หรือการสร้าง container แบบกำหนดเอง คุณสามารถสร้างระบบการจัดการ dependency ที่แข็งแกร่งและยืดหยุ่น ซึ่งช่วยปรับปรุงคุณภาพโดยรวมและความสามารถในการบำรุงรักษาแอปพลิเคชัน FastAPI ของคุณ
ข้อควรพิจารณาสำหรับแอปพลิเคชันระดับโลก
เมื่อออกแบบ DI container สำหรับแอปพลิเคชันระดับโลก สิ่งสำคัญที่ต้องพิจารณาคือ:
- Localization: Dependencies ที่เกี่ยวข้องกับการแปลภาษา (เช่น การตั้งค่าภาษา, รูปแบบวันที่) ควรได้รับการจัดการโดย DI container เพื่อให้แน่ใจว่ามีความสอดคล้องกันในแต่ละภูมิภาค
- Time Zones: Dependencies ที่จัดการการแปลงเขตเวลาควรถูก inject เข้าไปเพื่อหลีกเลี่ยงการ hardcode ข้อมูลเขตเวลา
- Currency: Dependencies สำหรับการแปลงและจัดรูปแบบสกุลเงินควรได้รับการจัดการโดย container เพื่อรองรับสกุลเงินต่างๆ
- Regional Settings: การตั้งค่าระดับภูมิภาคอื่นๆ เช่น รูปแบบตัวเลขและรูปแบบที่อยู่ ควรได้รับการจัดการโดย DI container เช่นกัน
- Multi-tenancy: สำหรับแอปพลิเคชันแบบ multi-tenant, DI container ควรสามารถให้ dependencies ที่แตกต่างกันสำหรับ tenant ที่ต่างกันได้ ซึ่งสามารถทำได้โดยใช้ scopes หรือตรรกะการ resolve dependency แบบกำหนดเอง
- Compliance and Security: ตรวจสอบให้แน่ใจว่ากลยุทธ์การจัดการ dependency ของคุณเป็นไปตามกฎระเบียบด้านความเป็นส่วนตัวของข้อมูลที่เกี่ยวข้อง (เช่น GDPR, CCPA) และแนวทางปฏิบัติด้านความปลอดภัยที่ดีที่สุดในภูมิภาคต่างๆ จัดการข้อมูลลับและค่ากำหนดต่างๆ อย่างปลอดภัยภายใน container
ด้วยการพิจารณาปัจจัยระดับโลกเหล่านี้ คุณสามารถสร้าง DI container ที่เหมาะสมอย่างยิ่งสำหรับการสร้างแอปพลิเคชันที่ทำงานในสภาพแวดล้อมระดับโลกได้